package ar.uba.fi.RSACryptoFiuba.view.decrypt;

import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.LayoutStyle.ComponentPlacement;

import org.apache.commons.lang3.math.NumberUtils;

import ar.uba.fi.RSACryptoFiuba.model.InvalidValueException;
import ar.uba.fi.RSACryptoFiuba.model.RSAAlgorithmEvaluationStrategy;
import ar.uba.fi.RSACryptoFiuba.model.RSAAlgorithmLearningStrategy;
import ar.uba.fi.RSACryptoFiuba.model.decrypt.RSADecryptAlgorithm;
import ar.uba.fi.RSACryptoFiuba.model.decrypt.RSADecryptMessage;
import ar.uba.fi.RSACryptoFiuba.model.encrypt.RSAEncryptMessage;
import ar.uba.fi.RSACryptoFiuba.model.keygeneration.RSAKeyGenerationMessage;
import ar.uba.fi.RSACryptoFiuba.view.commons.ButtonsPanel;
import ar.uba.fi.RSACryptoFiuba.view.commons.EvaluationInteractionPanel;
import ar.uba.fi.RSACryptoFiuba.view.commons.ResultsPanel;


@SuppressWarnings("serial")
public class DecryptEvaluationPanel extends JPanel implements Observer {
	
	private DecryptProgressPanel progressPanel;
		
	private JPanel cards;
	private CardLayout cardsLayout;
	private DecryptConfigurationPanel decryptConfigurationPanel;
	private EvaluationInteractionPanel generateNumericalRepresentationPanel;
	private List<EvaluationInteractionPanel> decryptPanels;
	private ResultsPanel decryptResultsPanel;
	private JSeparator lineSeparator;
	private ButtonsPanel buttonsPanel;
            
    private RSADecryptAlgorithm algorithm;
    private RSADecryptMessage message;
    private boolean isAlgorithmConfigurated;
    
	private RSAAlgorithmEvaluationStrategy evaluationStrategy;
	private RSAAlgorithmLearningStrategy learningStrategy;
    	
	public DecryptEvaluationPanel(ActionListener modeButtonActionListener, RSAKeyGenerationMessage keyGenerationMessage, RSAEncryptMessage encryptMessage, RSADecryptMessage decryptMessage){
		this.message = decryptMessage;
		this.evaluationStrategy = new RSAAlgorithmEvaluationStrategy();
		this.learningStrategy = new RSAAlgorithmLearningStrategy();
		initComponentes(modeButtonActionListener, keyGenerationMessage, encryptMessage);
	}
	
	private void initComponentes(ActionListener modeButtonActionListener, RSAKeyGenerationMessage keyGenerationMessage, RSAEncryptMessage encryptMessage){		
		
		//Progress Panel
		progressPanel = new DecryptProgressPanel();
                       
        //Step Panels
        cards = new JPanel();
        cards.setPreferredSize(new Dimension(670, 165));
        
        cardsLayout = new CardLayout();
        cards.setLayout(cardsLayout);
        
        decryptConfigurationPanel = new DecryptConfigurationPanel(keyGenerationMessage, encryptMessage);
        cards.add(decryptConfigurationPanel, "DECRYPT_CONFIGURATION");
            
		//Buttons Panel
        lineSeparator = new JSeparator();        

        ActionListener firstButtonActionListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				resetCards();
				decryptConfigurationPanel.resetErrors();
				if(!algorithm.getCurrentStep().isFirst()){
	    			algorithm.reset();
        		}
				cardsLayout.first(cards);
				isAlgorithmConfigurated = false;
				handleButtonsEnable();
			}
		};  

		ActionListener previousButtonActionListener = new ActionListener() {
	    	public void actionPerformed(ActionEvent e) {
	    		if(!algorithm.getCurrentStep().isFirst()){   			
	    			algorithm.movePreviousStep();
	    			algorithm.undoStep();
	    		}else{
	    			isAlgorithmConfigurated = false;
	    			decryptConfigurationPanel.resetErrors();
	    		}
        		cardsLayout.previous(cards);
        		resetCards();	    		
	    		handleButtonsEnable();
	    	}
	    };

	    ActionListener nextButtonActionListener = new ActionListener() {
        	public void actionPerformed(ActionEvent e) {
        		if(!isAlgorithmConfigurated){
        			configurateAlgorithm();
        			if(isAlgorithmConfigurated){          		
        				cardsLayout.next(cards);
        				handleButtonsEnable();
        				decryptConfigurationPanel.resetErrors();
        				algorithm.getMessage().update();
        			}
        		} else {
	        		JPanel card = getCurrentCard();
	        		if(card instanceof EvaluationInteractionPanel){
	        			((EvaluationInteractionPanel) card).resetToolTip();
	        			algorithm.getMessage().setElementToEvaluate(((EvaluationInteractionPanel) card).getOperationValue());
	        		}	        	       
					try {
						algorithm.executeStep();
						algorithm.moveNextStep();
						cardsLayout.next(cards);
						resetCards();
					} catch (InvalidValueException ex) {
						if (card instanceof EvaluationInteractionPanel) {
							((EvaluationInteractionPanel) card).setToolTip(ex.getMessage());
						}

					}
					handleButtonsEnable();
        		}
        	}	
        };

        ActionListener lastButtonActionListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if(!isAlgorithmConfigurated){
        			configurateAlgorithm();
        			if(isAlgorithmConfigurated){          		
        				resolve();
        			}        			
        		} else {        			
        			resolve();
        		}			
			}
			
			private void resolve(){
				if(!algorithm.getCurrentStep().isLast()){
    				algorithm.setStrategy(learningStrategy);
    				algorithm.resolve();
    				cardsLayout.last(cards);
    				algorithm.undoStep();
    				algorithm.setStrategy(evaluationStrategy);
    			}
    			handleButtonsEnable();
			}
		};
	  
		buttonsPanel = new ButtonsPanel(modeButtonActionListener, firstButtonActionListener, previousButtonActionListener, nextButtonActionListener, lastButtonActionListener);
		  
		//--------------------------------

		GroupLayout decryptTabLayout = new GroupLayout(this);
        this.setLayout(decryptTabLayout);
        decryptTabLayout.setHorizontalGroup(
            decryptTabLayout.createParallelGroup(Alignment.LEADING)
            .addGroup(decryptTabLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(decryptTabLayout.createParallelGroup(Alignment.LEADING)
                    .addComponent(lineSeparator, Alignment.TRAILING)
                    .addComponent(progressPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(cards, GroupLayout.DEFAULT_SIZE, 830, Short.MAX_VALUE)
                    .addComponent(buttonsPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        decryptTabLayout.setVerticalGroup(
            decryptTabLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(decryptTabLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(progressPanel, GroupLayout.PREFERRED_SIZE, 81, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(cards, GroupLayout.DEFAULT_SIZE, 159, Short.MAX_VALUE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(lineSeparator, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.UNRELATED)
                .addComponent(buttonsPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addGap(6, 6, 6))
        );
	}

	public void update(Observable o, Object arg) {
		RSADecryptMessage message = (RSADecryptMessage) o;
		
		String encryptedText = message.getEncryptedText();
		String privateKey = String.format("(%s, %s)", message.getDKey(), message.getNKey());
		if(!message.getNumericalRepresentation().isEmpty()){
			String processed = message.getProcessedEncryptedText();
			String remain = message.getRemainEncryptedText();					
			encryptedText = " [" + processed + "] " + remain;
		}	
		this.progressPanel.setPrivateKeyValue(privateKey);
		this.progressPanel.setCryptogramValue(encryptedText);
		this.progressPanel.setNumericalRepresentationValue(message.getNumericalRepresentation());
		this.progressPanel.setMessageValue(message.getPlainText());
		
		decryptConfigurationPanel.setDValue(message.getDKey());
		decryptConfigurationPanel.setNValue(message.getNKey());
		decryptConfigurationPanel.setCryptogramValue(message.getEncryptedText());
			
		
		for (int i = 0; i < decryptPanels.size(); i++) {
			decryptPanels.get(i).setOperationValue(message.getActualM());
		}
		generateNumericalRepresentationPanel.setOperationValue(message.getPlainText());      
		
		decryptResultsPanel.setResultsValues("Criptograma", "Mensaje", message.getEncryptedText(), message.getPlainText());
        
        handleButtonsEnable();		
	}
		
	public void handleButtonsEnable(){
		this.buttonsPanel.getFirstButton().setEnabled(isAlgorithmConfigurated);
		this.buttonsPanel.getPreviousButton().setEnabled(isAlgorithmConfigurated);
		this.buttonsPanel.getNextButton().setEnabled(!algorithm.getCurrentStep().isLast());
		this.buttonsPanel.getLastButton().setEnabled(!algorithm.getCurrentStep().isLast());
	}
	
	public void configurateAlgorithm() {
		String d = decryptConfigurationPanel.getDValue();
		String n = decryptConfigurationPanel.getNValue();
		String cryptogram = decryptConfigurationPanel.getCryptogramValue();
		if(areValidParameters(d, n, cryptogram)){
			message.setDKey(d);
			message.setNKey(n);
			message.setEncryptedText(cryptogram);
			message.setRemainEncryptedText(cryptogram);
			this.algorithm = new RSADecryptAlgorithm(evaluationStrategy, message);
			this.algorithm.getMessage().addObserver(this);
			isAlgorithmConfigurated = true;
			decryptPanels = new ArrayList<EvaluationInteractionPanel>();
			for (int i = 0; i < algorithm.getDecryptionSteps(); i++) {
				EvaluationInteractionPanel encryptPanel = new EvaluationInteractionPanel("m:");
				decryptPanels.add(encryptPanel);
			}
			
			List<Component> componentsToRemove = new ArrayList<Component>();
	        for (int i = 1; i < cards.getComponents().length; i++) {
				componentsToRemove.add(cards.getComponent(i));
			}
	        for (Component component : componentsToRemove) {
				cards.remove(component);
			}
	        
			for (int i = 0; i < decryptPanels.size(); i++) {
				cards.add(decryptPanels.get(i), "DECRYPT_"+i);
			}
			
			generateNumericalRepresentationPanel = new EvaluationInteractionPanel("C:");
	        cards.add(generateNumericalRepresentationPanel, "GENERATE_NUMERICAL_REPRESENTATION");
			
			decryptResultsPanel = new ResultsPanel();
	        cards.add(decryptResultsPanel, "DECRYPT_RESULTS");
	        this.isAlgorithmConfigurated = true;
		} else {
			decryptConfigurationPanel.setErrors();
		}
	}
	
	private boolean areValidParameters(String d, String n, String cryptogram) {
		return NumberUtils.isDigits(d) && NumberUtils.isDigits(n) && NumberUtils.isDigits(cryptogram) && (cryptogram.length() % n.length() == 0);
		
	}
	
	private JPanel getCurrentCard(){ 
		JPanel currentCard = null;
		for (Component component : cards.getComponents()) {
			if (component.isVisible()) {
				currentCard = (JPanel) component;
				break;
			}
		}
		return currentCard;
	}
	

	private void resetCards() {
		JPanel card = getCurrentCard();
		if(card instanceof EvaluationInteractionPanel){
			((EvaluationInteractionPanel) card).setOperationValue("");
			((EvaluationInteractionPanel) card).resetToolTip();
		}
	}
	
}	

